﻿using System;
using System.Collections.Generic;
using System.Device.Location;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;

namespace ChinaGeo
{
    internal static class Cache
    {
        const string DbFilename = @"ChinaGeoCache.bin";

        readonly private static object asyncRoot = new object();

        private static List<ChinaGeoModel> list = new List<ChinaGeoModel>();

        static Cache()
        {
            using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
                if (isf.FileExists(DbFilename))
                    using (var stream = isf.OpenFile(DbFilename, FileMode.Open))
                    using (var reader = new BinaryReader(stream))
                    {
                        if (stream.Length % 40 != 0)
                            stream.SetLength(stream.Length / 40 * 40);

                        while (stream.Position < stream.Length)
                        {
                            var model = new ChinaGeoModel(reader.ReadDouble(),
                                reader.ReadDouble(),
                                reader.ReadDouble(),
                                reader.ReadDouble(),
                                new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero).ToLocalTime()
                            );
                            model.Cached = true;
                            list.Add(model);
                        }
                    }
        }

        public static void Add(GeoCoordinate earth)
        {
            lock (asyncRoot)
                list.Add(new ChinaGeoModel(earth.Copy(), null));
        }

        public static void Update(GeoCoordinate earth, GeoCoordinate china)
        {
            lock (asyncRoot)
            {
                var model = list.FirstOrDefault(s => s.EarthGeo.Latitude == earth.Latitude && s.EarthGeo.Longitude == earth.Longitude);
                if (model != null)
                    model.UpdateChinaGeo(china);
                else
                    list.Add(new ChinaGeoModel(earth, china));
            }
        }

        public static ChinaGeoModel Get(GeoCoordinate earth)
        {
            lock (asyncRoot)
                return list.MinT(s => s.EarthGeo.GetDistanceTo(earth));
        }
        public static ChinaGeoModel Get(GeoCoordinate earth, out double dist)
        {
            lock (asyncRoot)
            {
                var st = list.MinT(s => s.EarthGeo.GetDistanceTo(earth));
                if (st == null)
                {
                    dist = double.NaN;
                    return null;
                }
                else
                {
                    dist = st.EarthGeo.GetDistanceTo(earth);
                    return st.Copy();
                }
            }
        }

        public static void SaveToDb(bool incremental = true)
        {
            using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
            using (var stream = isf.OpenFile(DbFilename, FileMode.OpenOrCreate))
            using (var writer = new BinaryWriter(stream))
            {
                var pending = list;
                if (incremental)
                {
                    stream.Position = stream.Length;
                    lock (asyncRoot)
                        pending = list.Where(s => !s.Cached && s.IsEffected).ToList();
                }
                else
                {
                    lock (asyncRoot)
                        pending = list.ToList();
                }
                foreach (var model in pending)
                {
                    writer.Write(model.EarthGeo.Latitude);
                    writer.Write(model.EarthGeo.Longitude);
                    writer.Write(model.ChinaGeo.Latitude);
                    writer.Write(model.ChinaGeo.Longitude);
                    writer.Write(model.Timestamp.UtcTicks);

                    model.Cached = true;
                }
                writer.Flush();
            }
        }
    }
}
